{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "view-in-github"
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/Aithu-Snehith/End-to-End-Learning-of-Communications-Systems-Without-a-Channel-Model/blob/master/8_pam_model.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "tkQuDDdaQAS0"
   },
   "source": [
    "## Importing Libraries required for the implementation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "YD6Q1OO06bA2"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import matplotlib.pyplot as plt\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras.layers import *\n",
    "from sklearn import preprocessing\n",
    "import tensorflow.keras.backend as K\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from sklearn.metrics import accuracy_score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "b3w26DLfQKIU"
   },
   "source": [
    "## Hyper Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Mamp_sR48wBd"
   },
   "outputs": [],
   "source": [
    "#length of message space\n",
    "msg_total = 8\n",
    "# number of channels\n",
    "channel = 16\n",
    "# number of epochs\n",
    "epochs = 10000\n",
    "# peturbation variance\n",
    "sigma = 1e-4\n",
    "# Batch size\n",
    "batch_size = 1024"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "jaQMKgqwQUy3"
   },
   "source": [
    "## Defining Required Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "cUhpbsruIeoE"
   },
   "outputs": [],
   "source": [
    "# Peturbation Sampling\n",
    "def perturbation(x):\n",
    "    w = K.random_normal(shape = (channel,2), mean=0.0,stddev=sigma**0.5,dtype=None)\n",
    "    xp = ((1-sigma)**0.5)*x + w\n",
    "    return xp\n",
    "\n",
    "# Defining transmitter loss\n",
    "def loss_tx(y_true, y_pred):\n",
    "    return -y_true*y_pred\n",
    "\n",
    "# Defining the policy\n",
    "def get_policy(inp):\n",
    "    xp = inp[0]\n",
    "    x = inp[1]\n",
    "    w = xp - x\n",
    "    policy = -K.sum(w*w)\n",
    "    return policy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nyV3UMsbQZ2A"
   },
   "source": [
    "# Transmitter Model\n",
    "\n",
    "## Defining Architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 88
    },
    "colab_type": "code",
    "id": "62RZYX6p-Q91",
    "outputId": "2eeaafb9-5221-4f23-e6dd-cf53981eb269"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Colocations handled automatically by placer.\n"
     ]
    }
   ],
   "source": [
    "tx_inp = Input((1,))\n",
    "# Adding embedding layer\n",
    "embbedings_layer = Dense(msg_total, activation = 'relu')(tx_inp)\n",
    "layer_dense = Dense(2*channel, activation = 'relu')(embbedings_layer)\n",
    "# real to complex \n",
    "to_complex = Reshape((channel,2))(layer_dense)\n",
    "# Normalising the output to unit energy\n",
    "x = Lambda(lambda x: keras.backend.l2_normalize(x))(to_complex)\n",
    "# Peturbation sampling \n",
    "xp = Lambda(perturbation)(to_complex)\n",
    "policy = Lambda(get_policy)([xp,x])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "KY_LiujYRGnW"
   },
   "source": [
    "## Creating Required models ( outputs from required layers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 408
    },
    "colab_type": "code",
    "id": "tdq7Oou4J1ZL",
    "outputId": "cb1dd314-902e-4477-dc8f-d9a3cb456ff8"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_1 (InputLayer)            (None, 1)            0                                            \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 8)            16          input_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "dense_1 (Dense)                 (None, 32)           288         dense[0][0]                      \n",
      "__________________________________________________________________________________________________\n",
      "reshape (Reshape)               (None, 16, 2)        0           dense_1[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_1 (Lambda)               (None, 16, 2)        0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda (Lambda)                 (None, 16, 2)        0           reshape[0][0]                    \n",
      "__________________________________________________________________________________________________\n",
      "lambda_2 (Lambda)               ()                   0           lambda_1[0][0]                   \n",
      "                                                                 lambda[0][0]                     \n",
      "==================================================================================================\n",
      "Total params: 304\n",
      "Trainable params: 304\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "# model for policy training\n",
    "model_policy = keras.models.Model(inputs=tx_inp, outputs=policy)\n",
    "# model to get the peturbatated output\n",
    "model_tx = keras.models.Model(inputs=tx_inp, outputs=xp)\n",
    "# model to get the encoded message to transmit\n",
    "model_x = keras.models.Model(inputs=tx_inp, outputs=x)\n",
    "\n",
    "model_policy.compile(loss=loss_tx, optimizer=tf.keras.optimizers.SGD(lr = 1e-5))\n",
    "print(model_policy.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "etyxVLIfRQCW"
   },
   "source": [
    "# Receiver\n",
    "\n",
    "## Defining Architecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 289
    },
    "colab_type": "code",
    "id": "jPJKGpLLLqbo",
    "outputId": "519e07be-841e-4789-e0cc-d28dcebf7187"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_2 (InputLayer)         (None, 16, 2)             0         \n",
      "_________________________________________________________________\n",
      "reshape_1 (Reshape)          (None, 32)                0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 256)               8448      \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 8)                 2056      \n",
      "=================================================================\n",
      "Total params: 10,504\n",
      "Trainable params: 10,504\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "rx_inp = Input((channel,2))\n",
    "# complex to real\n",
    "to_flat = Reshape((2*channel,))(rx_inp)\n",
    "fc = Dense(8*2*channel, activation = 'relu')(to_flat)\n",
    "softmax = Dense(msg_total, activation = 'softmax')(fc)\n",
    "\n",
    "model_rx = keras.models.Model(inputs=rx_inp, outputs=softmax)\n",
    "\n",
    "model_rx.compile(loss=tf.keras.losses.categorical_crossentropy, optimizer=tf.keras.optimizers.Adam())\n",
    "print(model_rx.summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "g4DMGmXSUk_h"
   },
   "source": [
    "## Alternative Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1788
    },
    "colab_type": "code",
    "id": "g6h-Vx6JpksG",
    "outputId": "9e1879e2-b98f-4837-8fad-a02b6cfeba1a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Use tf.cast instead.\n",
      "epoch:  0 tx_loss 111.65034484863281 rx_loss 2.075446128845215\n",
      "epoch:  100 tx_loss 4.3518385887146 rx_loss 1.5956542491912842\n",
      "epoch:  200 tx_loss 3.1458160877227783 rx_loss 1.3446216583251953\n",
      "epoch:  300 tx_loss 2.183445930480957 rx_loss 1.0681068897247314\n",
      "epoch:  400 tx_loss 2.750500440597534 rx_loss 0.8244391679763794\n",
      "epoch:  500 tx_loss 3.4527690410614014 rx_loss 0.6615206003189087\n",
      "epoch:  600 tx_loss 3.9687094688415527 rx_loss 0.5484654307365417\n",
      "epoch:  700 tx_loss 4.75673246383667 rx_loss 0.44556933641433716\n",
      "epoch:  800 tx_loss 2.3664300441741943 rx_loss 0.4057242274284363\n",
      "epoch:  900 tx_loss 3.823850393295288 rx_loss 0.34978848695755005\n",
      "epoch:  1000 tx_loss 4.562586784362793 rx_loss 0.3229084312915802\n",
      "epoch:  1100 tx_loss 5.9120283126831055 rx_loss 0.2832150459289551\n",
      "epoch:  1200 tx_loss 3.35794734954834 rx_loss 0.2697519361972809\n",
      "epoch:  1300 tx_loss 2.7322275638580322 rx_loss 0.21645428240299225\n",
      "epoch:  1400 tx_loss 4.766214370727539 rx_loss 0.21287575364112854\n",
      "epoch:  1500 tx_loss 2.972025156021118 rx_loss 0.20674437284469604\n",
      "epoch:  1600 tx_loss 3.2371692657470703 rx_loss 0.1805959790945053\n",
      "epoch:  1700 tx_loss 3.3259565830230713 rx_loss 0.18627703189849854\n",
      "epoch:  1800 tx_loss 2.6115152835845947 rx_loss 0.15560747683048248\n",
      "epoch:  1900 tx_loss 4.833925247192383 rx_loss 0.13701024651527405\n",
      "epoch:  2000 tx_loss 2.7226548194885254 rx_loss 0.12894144654273987\n",
      "epoch:  2100 tx_loss 7.077600955963135 rx_loss 0.10896340012550354\n",
      "epoch:  2200 tx_loss 4.855541229248047 rx_loss 0.09421651065349579\n",
      "epoch:  2300 tx_loss 3.9910638332366943 rx_loss 0.10274013876914978\n",
      "epoch:  2400 tx_loss 3.944584369659424 rx_loss 0.05820135027170181\n",
      "epoch:  2500 tx_loss 4.085627555847168 rx_loss 0.06760348379611969\n",
      "epoch:  2600 tx_loss 4.186509132385254 rx_loss 0.043666109442710876\n",
      "epoch:  2700 tx_loss 4.096609592437744 rx_loss 0.039834339171648026\n",
      "epoch:  2800 tx_loss 3.936901807785034 rx_loss 0.04208402335643768\n",
      "epoch:  2900 tx_loss 3.0933609008789062 rx_loss 0.031584665179252625\n",
      "epoch:  3000 tx_loss 2.5114285945892334 rx_loss 0.03643415868282318\n",
      "epoch:  3100 tx_loss 3.968400716781616 rx_loss 0.03069741651415825\n",
      "epoch:  3200 tx_loss 4.790694713592529 rx_loss 0.039969541132450104\n",
      "epoch:  3300 tx_loss 5.108456611633301 rx_loss 0.03195992484688759\n",
      "epoch:  3400 tx_loss 5.536928176879883 rx_loss 0.027466170489788055\n",
      "epoch:  3500 tx_loss 5.189927577972412 rx_loss 0.023680424317717552\n",
      "epoch:  3600 tx_loss 5.032960414886475 rx_loss 0.019849564880132675\n",
      "epoch:  3700 tx_loss 5.154823303222656 rx_loss 0.01682446338236332\n",
      "epoch:  3800 tx_loss 6.82951021194458 rx_loss 0.017644288018345833\n",
      "epoch:  3900 tx_loss 6.10154914855957 rx_loss 0.013960335403680801\n",
      "epoch:  4000 tx_loss 6.852365493774414 rx_loss 0.015919698402285576\n",
      "epoch:  4100 tx_loss 4.90704345703125 rx_loss 0.015047567896544933\n",
      "epoch:  4200 tx_loss 5.894028663635254 rx_loss 0.010916702449321747\n",
      "epoch:  4300 tx_loss 3.0122227668762207 rx_loss 0.010609409771859646\n",
      "epoch:  4400 tx_loss 4.682041168212891 rx_loss 0.012999525293707848\n",
      "epoch:  4500 tx_loss 8.213058471679688 rx_loss 0.008963012136518955\n",
      "epoch:  4600 tx_loss 4.626161575317383 rx_loss 0.007602107245475054\n",
      "epoch:  4700 tx_loss 3.7452304363250732 rx_loss 0.007352523505687714\n",
      "epoch:  4800 tx_loss 6.248739242553711 rx_loss 0.0069618928246200085\n",
      "epoch:  4900 tx_loss 4.939318656921387 rx_loss 0.010771127417683601\n",
      "epoch:  5000 tx_loss 4.491368293762207 rx_loss 0.008541966788470745\n",
      "epoch:  5100 tx_loss 5.126115798950195 rx_loss 0.005239963997155428\n",
      "epoch:  5200 tx_loss 5.8089118003845215 rx_loss 0.004610837437212467\n",
      "epoch:  5300 tx_loss 2.4534215927124023 rx_loss 0.005705135874450207\n",
      "epoch:  5400 tx_loss 6.818317890167236 rx_loss 0.006106524262577295\n",
      "epoch:  5500 tx_loss 4.380892276763916 rx_loss 0.005216584540903568\n",
      "epoch:  5600 tx_loss 3.577620506286621 rx_loss 0.006254799664020538\n",
      "epoch:  5700 tx_loss 6.1088457107543945 rx_loss 0.002732411725446582\n",
      "epoch:  5800 tx_loss 7.872372627258301 rx_loss 0.006918097846210003\n",
      "epoch:  5900 tx_loss 5.95105504989624 rx_loss 0.0022812550887465477\n",
      "epoch:  6000 tx_loss 4.555535793304443 rx_loss 0.0022870583925396204\n",
      "epoch:  6100 tx_loss 4.640500068664551 rx_loss 0.010331875644624233\n",
      "epoch:  6200 tx_loss 3.6990368366241455 rx_loss 0.0021935012191534042\n",
      "epoch:  6300 tx_loss 4.898043155670166 rx_loss 0.0020838286727666855\n",
      "epoch:  6400 tx_loss 5.4040446281433105 rx_loss 0.0017388788983225822\n",
      "epoch:  6500 tx_loss 5.057991981506348 rx_loss 0.002344192937016487\n",
      "epoch:  6600 tx_loss 6.815639019012451 rx_loss 0.002434772439301014\n",
      "epoch:  6700 tx_loss 4.923605918884277 rx_loss 0.0021747329737991095\n",
      "epoch:  6800 tx_loss 4.367935657501221 rx_loss 0.001837260671891272\n",
      "epoch:  6900 tx_loss 5.003668785095215 rx_loss 0.0030599073506891727\n",
      "epoch:  7000 tx_loss 6.880077838897705 rx_loss 0.001666948664933443\n",
      "epoch:  7100 tx_loss 4.616969108581543 rx_loss 0.006662178318947554\n",
      "epoch:  7200 tx_loss 4.946462631225586 rx_loss 0.005256841890513897\n",
      "epoch:  7300 tx_loss 4.189868450164795 rx_loss 0.001434624777175486\n",
      "epoch:  7400 tx_loss 5.299427032470703 rx_loss 0.0010790545493364334\n",
      "epoch:  7500 tx_loss 2.579153299331665 rx_loss 0.0010170130990445614\n",
      "epoch:  7600 tx_loss 5.300052642822266 rx_loss 0.0006194217712618411\n",
      "epoch:  7700 tx_loss 3.060616970062256 rx_loss 0.001046905410476029\n",
      "epoch:  7800 tx_loss 3.76302433013916 rx_loss 0.0010550954611971974\n",
      "epoch:  7900 tx_loss 4.558821201324463 rx_loss 0.000819277367554605\n",
      "epoch:  8000 tx_loss 2.506747245788574 rx_loss 0.0001316565030720085\n",
      "epoch:  8100 tx_loss 5.678400039672852 rx_loss 0.0002782098308671266\n",
      "epoch:  8200 tx_loss 4.359503269195557 rx_loss 0.0002059138787444681\n",
      "epoch:  8300 tx_loss 3.469759702682495 rx_loss 0.0005165160982869565\n",
      "epoch:  8400 tx_loss 4.149544715881348 rx_loss 0.0011060985270887613\n",
      "epoch:  8500 tx_loss 4.888036251068115 rx_loss 0.005483447108417749\n",
      "epoch:  8600 tx_loss 5.130725860595703 rx_loss 0.00027724370011128485\n",
      "epoch:  8700 tx_loss 3.6243844032287598 rx_loss 0.0018223000224679708\n",
      "epoch:  8800 tx_loss 5.096489429473877 rx_loss 0.0002794914471451193\n",
      "epoch:  8900 tx_loss 4.828254222869873 rx_loss 0.0002615988196339458\n",
      "epoch:  9000 tx_loss 4.805609703063965 rx_loss 0.0010482058860361576\n",
      "epoch:  9100 tx_loss 3.2759108543395996 rx_loss 0.0007440315675921738\n",
      "epoch:  9200 tx_loss 7.272687911987305 rx_loss 0.0002482463023625314\n",
      "epoch:  9300 tx_loss 3.771700143814087 rx_loss 0.0002673517738003284\n",
      "epoch:  9400 tx_loss 4.501392364501953 rx_loss 0.00035931519232690334\n",
      "epoch:  9500 tx_loss 6.863140106201172 rx_loss 0.00046710309106856585\n",
      "epoch:  9600 tx_loss 5.743268966674805 rx_loss 0.0016094766324386\n",
      "epoch:  9700 tx_loss 4.276912689208984 rx_loss 0.00021542655304074287\n",
      "epoch:  9800 tx_loss 5.513436794281006 rx_loss 0.00012897647684440017\n",
      "epoch:  9900 tx_loss 4.092998027801514 rx_loss 0.00040719134267419577\n"
     ]
    }
   ],
   "source": [
    "loss_tx = []\n",
    "loss_rx = []\n",
    "for epoch in range(epochs):\n",
    "#     trasmitter training\n",
    "#     generating input\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "#     Generating labels\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "#     Transmitter prediction ( message encoding )\n",
    "    xp = model_tx.predict(tx_input)\n",
    "#     Adding noise ( modelling AWGN layer)\n",
    "    y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "#     Decoding the message\n",
    "    pred = model_rx.predict(y)\n",
    "#     Getting loss\n",
    "    loss = np.sum(np.square(label - pred), axis = 1)\n",
    "#     Transmitter model training\n",
    "    history_tx = model_policy.fit(tx_input, loss, batch_size=batch_size, epochs=1, verbose=0)    \n",
    "    loss_tx.append(history_tx.history['loss'][0])\n",
    "    \n",
    "#     Receiver Training\n",
    "    raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "    label = np.zeros((batch_size, msg_total))\n",
    "    label[np.arange(batch_size), raw_input] = 1\n",
    "    tx_input = raw_input/float(msg_total)\n",
    "    x = model_x.predict(tx_input)\n",
    "    y = x + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "    history_rx = model_rx.fit(y, label, batch_size=batch_size, epochs=1, verbose=0)\n",
    "    loss_rx.append(history_rx.history['loss'][0])\n",
    "    \n",
    "#     Printing only after 100 epochs\n",
    "    if(epoch % 100 == 0):\n",
    "        print('epoch: ', epoch, 'tx_loss', history_tx.history['loss'][0], 'rx_loss', history_rx.history['loss'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qEj8LifcVAPQ"
   },
   "source": [
    "## Plotting the Transmitter and Receiver Loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 350
    },
    "colab_type": "code",
    "id": "YOax3T0THnrF",
    "outputId": "b69bfb7e-4727-4fe9-96a8-500f6a3d1258"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x360 with 2 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize = (12,5))\n",
    "plt.subplot(1,2,1)\n",
    "plt.plot(loss_tx)\n",
    "plt.title('Transmitter Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.subplot(1,2,2)\n",
    "plt.plot(loss_rx)\n",
    "plt.title('Receiver Loss')\n",
    "plt.xlabel('epochs')\n",
    "plt.ylabel('loss')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "jvaDNhLiVbIJ"
   },
   "source": [
    "## Prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 136
    },
    "colab_type": "code",
    "id": "Ob52RzJevSF2",
    "outputId": "c0e2edb1-380e-40ae-9c33-83c33df21b53"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Transmitted Signal: [3 1 0 2 5 3 7 3 6 4 3 6 3 2 2 7 1 0 4 4 2 6 7 1 5 7 0 2 0 6 6 2 2 0 6 4 1\n",
      " 6 7 5 7 2 3 3 0 4 1 2 6 3 4 3 3 3 4 3 5 3 3 0 7 7 4 4 1 6 2 2 5 4 2 4 3 0\n",
      " 0 1 4 0 2 4 6 6 6 5 6 5 6 0 5 2 7 5 1 4 3 3 6 1 5 2]\n",
      "Received Signal: [3 1 0 2 5 3 7 3 6 4 3 6 3 2 2 7 1 0 4 4 2 6 7 1 5 7 0 2 0 6 6 2 2 0 6 4 1\n",
      " 6 7 5 7 2 3 3 0 4 1 2 6 3 4 3 3 3 4 3 5 3 3 0 7 7 4 4 1 6 2 2 5 4 2 4 3 0\n",
      " 0 1 4 0 2 4 6 6 6 5 6 5 6 0 5 2 7 5 1 4 3 3 6 1 5 2]\n",
      "accuracy: 1.0\n"
     ]
    }
   ],
   "source": [
    "#testing\n",
    "batch_size = 100\n",
    "raw_input = np.random.randint(0,msg_total,(batch_size))\n",
    "print('Transmitted Signal:',raw_input)\n",
    "label = np.zeros((batch_size, msg_total))\n",
    "label[np.arange(batch_size), raw_input] = 1\n",
    "tx_input = raw_input/float(msg_total)\n",
    "xp = model_x.predict(tx_input)\n",
    "y = xp + np.random.normal(0,0.001,(batch_size, channel,2))\n",
    "pred = model_rx.predict(y)\n",
    "pred_int = np.argmax(pred, axis = 1)\n",
    "print('Received Signal:',pred_int)\n",
    "\n",
    "\n",
    "print('accuracy:',accuracy_score(raw_input, pred_int))"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "include_colab_link": true,
   "name": "8_pam_model.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
